package baseFx.common;

import baseFx.common.utils.ClassUtils;
import baseFx.common.utils.StringUtils;

import java.lang.reflect.Array;
import java.util.*;

public class Stream<IN> {
    private List<IN> data;

    public static <T> Stream<T> create(T... args) {
        List<T> list = new ArrayList<>(Arrays.asList(args));
        return new Stream<>(list);
    }

    public static <T> Stream<T> create(Collection<T> items) {
        List<T> list = new ArrayList<>(items);
        return new Stream<>(list);
    }

    public static <T> Stream<T> create(Enumeration<T> items) {
        List<T> list = new ArrayList<>();
        while (items.hasMoreElements()) {
            list.add(items.nextElement());
        }
        return new Stream<>(list);
    }

    Stream(List<IN> items) {
        data = items == null ? new ArrayList<>() : items;
    }

    public Stream<IN> filter(Func1T<IN, Boolean> func) {
        List<IN> list = new ArrayList<>();
        for (IN item : data) {
            if (func.execute(item)) {
                list.add(item);
            }
        }
        return new Stream<>(list);
    }

    public Stream<IN> filter() {
        return filter(Objects::nonNull);
    }

    public <OUT> Stream<OUT> map(Func1T<IN, OUT> func) {
        List<OUT> list = new ArrayList<>();
        for (IN item : data) {
            list.add(func.execute(item));
        }
        return new Stream<>(list);
    }

    public int count() {
        return count(null);
    }

    public int count(Func1T<IN, Boolean> func) {
        if (func == null) {
            return data.size();
        }
        int c = 0;
        for (IN item : data) {
            if (func.execute(item)) {
                c++;
            }
        }
        return c;
    }

    public Optional<IN> first(Func1T<IN, Boolean> func) {
        if (data.size() < 1) {
            return Optional.empty();
        }
        if (func == null) {
            return Optional.of(data.get(0));
        }
        for (IN item : data) {
            if (func.execute(item)) {
                return Optional.of(item);
            }
        }
        return Optional.empty();
    }

    public Optional<IN> first() {
        return first(null);
    }

    public Optional<IN> last(Func1T<IN, Boolean> func) {
        if (data.size() < 1) {
            return Optional.empty();
        }
        if (func == null) {
            return Optional.of(data.get(data.size() - 1));
        }
        for (int i = data.size() - 1; i >= 0; i--) {
            if (func.execute(data.get(i))) {
                return Optional.of(data.get(i));
            }
        }
        return Optional.empty();
    }

    public Optional<IN> last() {
        return last(null);
    }

    public List<IN> toList(Func1T<IN, Boolean> func) {
        List<IN> items = new ArrayList<>();
        if (func == null) {
            items.addAll(data);
        } else {
            for (IN item : data) {
                if (func.execute(item)) {
                    items.add(item);
                }
            }
        }
        return items;
    }

    public List<IN> toList() {
        return toList(null);
    }

    public IN[] toArray(Class<IN> clazz) {
        IN[] arr = (IN[]) Array.newInstance(clazz, data.size());
        for (int i = 0; i < arr.length; i++) {
            arr[i] = data.get(i);
        }
        return arr;
    }

    public Stream<IN> foreach(Action1T<IN> action) {
        List<IN> list = new ArrayList<>(data);
        for (IN item : list) {
            action.execute(item);
        }
        return this;
    }

    public Stream<IN> foreach(Action2T<Integer, IN> action) {
        List<IN> list = new ArrayList<>(data);
        for (int i = 0; i < list.size(); i++) {
            action.execute(i, list.get(i));
        }
        return this;
    }

    public Stream<IN> foreach(Action3T<Integer, Integer, IN> action) {
        List<IN> list = new ArrayList<>(data);
        for (int i = 0; i < list.size(); i++) {
            action.execute(list.size(), i, list.get(i));
        }
        return this;
    }

    public Stream<IN> foreach(Func2T<Integer, IN, Boolean> action) {
        List<IN> list = new ArrayList<>(data);
        for (int i = 0; i < list.size(); i++) {
            Boolean b = action.execute(i, list.get(i));
            if (b != null && b == false) {
                break;
            }
        }
        return this;
    }

    public boolean any(Func1T<IN, Boolean> func) {
        if (func == null) {
            return data.isEmpty() == false;
        }
        for (IN item : data) {
            if (func.execute(item)) {
                return true;
            }
        }
        return false;
    }

    public boolean any() {
        return any((Func1T<IN, Boolean>) null);
    }

    public boolean any(IN item) {
        if (item instanceof String) {
            String text = (String) item;
            return any(text::equals);
        } else {
            return any(i -> i == item);
        }
    }

    public boolean all(Func1T<IN, Boolean> func) {
        for (IN item : data) {
            if (func.execute(item) == false) {
                return false;
            }
        }
        return true;
    }

    public <K, V> Map<K, V> toMap(Func1T<IN, K> getKey, Func1T<IN, V> getValue) {
        Map<K, V> map = new LinkedHashMap<>();
        for (IN item : data) {
            K key = getKey.execute(item);
            V value = getValue.execute(item);
            if (map.put(key, value) != null) {
                throw new RuntimeException(key + "键已经存在，不能重复。");
            }
        }
        return map;
    }

    public <K> Map<K, IN> toMap(Func1T<IN, K> getKey) {
        return toMap(getKey, i -> i);
    }

    /**
     * 按给定的值进行分组
     */
    public StreamGroup<IN> group(Func1T<IN, String> func) {
        Map<String, List<IN>> map = new LinkedHashMap<>();
        for (IN item : data) {
            String k = func.execute(item);
            if (map.containsKey(k) == false) {
                map.put(k, new ArrayList<>());
            }
            map.get(k).add(item);
        }
        List<Group<IN>> groups = new ArrayList<>();
        for (String key : map.keySet()) {
            groups.add(new Group<>(key, map.get(key).get(0), map.get(key)));
        }
        return new StreamGroup<>(groups);
    }

    public static class Group<T> {
        private String rawkey;
        private T key;
        private Stream<T> data;

        Group(String raw, T key, List<T> items) {
            this.data = Stream.create(items);
            this.key = key;
            this.rawkey = raw;
        }

        public String rawKey() {
            return rawkey;
        }

        public T key() {
            return key;
        }

        public Stream<T> items() {
            return data;
        }

        public int size() {
            return data.count();
        }
    }

    /**
     * 翻转序列顺序
     */
    public Stream<IN> reverse() {
        List<IN> list = new ArrayList<>();
        for (int i = data.size() - 1; i >= 0; i--) {
            list.add(data.get(i));
        }
        return new Stream<>(list);
    }

    public Stream<IN> sort(Func2T<IN, IN, Boolean> comparison) {
        List<IN> list = new ArrayList<>(data);
        for (int i = 0; i < list.size() - 1; i++) {
            for (int j = 0; j < list.size() - i - 1; j++) {
                IN item = list.get(j);
                IN item2 = list.get(j + 1);
                if (comparison.execute(item, item2)) {
                    list.set(j, item2);
                    list.set(j + 1, item);
                } else {
                    list.set(j + 1, item2);
                    list.set(j, item);
                }
            }
        }
        return new Stream<>(list);
    }

    public Stream<IN> sort() {
        return sort((i1, i2) -> {
            if (i1 == null || i2 == null) {
                return false;
            }
            if (i1 instanceof Comparable) {
                return ((Comparable<IN>) i1).compareTo(i2) > 0;
            }
            return i1.toString().compareTo(i2.toString()) > 0;
        });
    }

    public <T> Stream<IN> sort(Func1T<IN, T> func) {
        return sort((i1, i2) -> {
            T o1 = func.execute(i1);
            T o2 = func.execute(i2);
            if (o1 == null || o2 == null) {
                return false;
            }
            if (o1 instanceof Comparable) {
                return ((Comparable<T>) o1).compareTo(o2) > 0;
            }
            return o1.toString().compareTo(o2.toString()) > 0;
        });
    }

    @Override
    public Stream<IN> clone() {
        return new Stream<>(new ArrayList<>(data));
    }

    /**
     * 剔除重复的元素
     */
    public Stream<IN> distinct() {
        List<IN> list = new ArrayList<>();
        for (IN item : data) {
            if (list.contains(item) == false) {
                list.add(item);
            }
        }
        return new Stream<>(list);
    }

    public <T> Stream<IN> distinct(Func1T<IN, T> func) {
        Map<T, IN> map = new LinkedHashMap<>();
        for (IN item : data) {
            T v = func.execute(item);
            if (map.containsKey(v) == false) {
                map.put(v, item);
            }
        }
        return Stream.create(map.values());
    }

    /**
     * 跳过指定数量的元素
     */
    public Stream<IN> skip(int count) {
        List<IN> list = new ArrayList<>();
        while (count < data.size()) {
            list.add(data.get(count++));
        }
        return new Stream<>(list);
    }

    /**
     * 提取指定数量的元素
     */
    public Stream<IN> take(int count) {
        List<IN> list = new ArrayList<>();
        for (int i = 0; i < count && i < data.size(); i++) {
            list.add(data.get(i));
        }
        return new Stream<>(list);
    }

    /**
     * 把指定元素序列合并
     */
    public <OUT> Stream<OUT> many(Func1T<IN, Collection<OUT>> func) {
        List<OUT> list = new ArrayList<>();
        for (IN item : data) {
            Collection<OUT> collection = func.execute(item);
            if (collection != null) {
                list.addAll(collection);
            }
        }
        return new Stream<>(list);
    }

    public Stream<IN> add(IN item) {
        data.add(item);
        return this;
    }

    public Stream<IN> add(Collection<IN> collection) {
        data.addAll(collection);
        return this;
    }

    public Stream<IN> remove(IN item) {
        data.remove(item);
        return this;
    }

    public Stream<IN> remove(Func1T<IN, Boolean> func) {
        List<IN> list = new ArrayList<>();
        for (IN item : data) {
            if (func.execute(item) == false) {
                list.add(item);
            }
        }
        data.clear();
        data = list;
        return this;
    }

    public IN max(Func1T<IN, Number> func) {
        if (data.isEmpty()) {
            return null;
        }
        double max = Double.MIN_VALUE;
        IN save = null;
        for (IN item : data) {
            double value = func.execute(item).doubleValue();
            if (value > max) {
                max = value;
                save = item;
            }
        }
        return save;
    }

    public IN min(Func1T<IN, Number> func) {
        if (data.isEmpty()) {
            return null;
        }
        double min = Double.MAX_VALUE;
        IN save = null;
        for (IN item : data) {
            double value = func.execute(item).doubleValue();
            if (value < min) {
                min = value;
                save = item;
            }
        }
        return save;
    }

    public long sum(Func1T<IN, Long> func) {
        long sum = 0;
        for (IN item : data) {
            Long num = func.execute(item);
            if (num != null) {
                sum += num;
            }
        }
        return sum;
    }

    public long avg(Func1T<IN, Long> func) {
        if (data.isEmpty()) {
            return 0;
        }
        long sum = sum(func);
        return sum / data.size();
    }

    public <T> Stream<T> cast(Class<T> clazz) {
        List<T> list = new ArrayList<>();
        for (IN item : data) {
            list.add((T) item);
        }
        return new Stream<>(list);
    }

    public <K> Map<K, List<IN>> mapGroup(Func1T<IN, K> func) {
        Map<K, List<IN>> map = new HashMap<>(64);
        for (IN item : data) {
            K key = func.execute(item);
            if (map.containsKey(key) == false) {
                map.put(key, new ArrayList<>());
            }
            map.get(key).add(item);
        }
        return map;
    }

    public String join(String split) {
        return StringUtils.join(split, data);
    }

    public Stream<IN> ifNotNull(Func1T<IN, Object> func) {
        return filter(i -> func.execute(i) != null);
    }

    public Stream<IN> ifNotNull() {
        return filter(i -> i != null);
    }

    public <T> Stream<T> map(Class<T> clazz, Action2T<T, IN> action) {
        return map(i -> {
            T model = ClassUtils.newInstance(clazz);
            action.execute(model, i);
            return model;
        });
    }

    public List<IN> removeRange(int index, int length) {
        List<IN> list = new ArrayList<>(length);
        for (int i = index; i < data.size() && i < index + length; i++) {
            list.add(data.remove(i));
        }
        return list;
    }

    public IN get(int index) {
        if (data.size() <= index) {
            throw new IndexOutOfBoundsException("错误的索引：" + index);
        }
        return data.get(index);
    }

    public IN get(int index, IN defaultValue) {
        if (data.size() <= index) {
            return defaultValue;
        }
        return data.get(index);
    }
}
